package mc0458.a;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

//线段树节点
class Tree {
	public int l; // 节点表示区间的左界
	public int r; // 节点表示区间的右届
	public int val; // 原始值的下标
	public int size; // 当前节点子树对应的数据大小

	public Tree(int l, int r) {
		this.l = l;
		this.r = r;
	}
}

public class Main {
	
	// 线段树原数组，下标从1开始计算
	public static int[] a;
	// 线段树，数组表示，长度必须为原数组4倍
	public static Tree[] t;

	// 返回左儿子编号，参数p为父节点编号
	public static int lc(int p) {
		// 左儿子编号p*2
		return p << 1; // 位运算提高效率，用p*2也行，更直观
	}

	// 返回右儿子编号，参数p为父节点编号
	public static int rc(int p) {
		// 右儿子编号p*2+1
		return p << 1 | 1; // 位运算提高效率，用p*2+1也行，更直观
	}

	// 构建线段树
	// 参数p节点编号
	// 参数l，r表示节点p指向的区间[l,r]
	// 初始化时调用build(1,L,R)
	public static void build(int p, int l, int r) {
		// 实例化节点
		t[p] = new Tree(l, r);
		// l==r则当前节点为叶子，直接存值
		// 叶子节点的l对应原数据的下标
		if (l == r) {
			// 此时递归到了叶子节点
			t[p].val = l; // 保存对应数据下标
			t[p].size = 1; // 初始叶子节点数据有效，size为1
			return;
		}
		// 折半找区间[l,r]的中点
		int mid = (l + r) >> 1; // 也可写(l+r)/2
		int lc = lc(p);
		int rc = rc(p);
		// 左儿子递归建树(递归建左子树[L,(L+R)/2])
		build(lc, l, mid);
		// 右儿子递归建树(递归建右子树[(L+R)/2]+1,R))
		build(rc, mid + 1, r);
		// 此处是递归回溯节点，当前节点左右子树已建完，依据左右子节点的值来算当前节点的值
		// 从下往上传递区间值，放入节点
		// 此处决定线段树维护区间和或最大值或最小值
		// 参数p是当前节点位置
		pushUp(p);
	}

	// 从下往上传递区间值，放入节点
	// 此处的p是叶子节点
	private static void pushUp(int p) {

		// 保存左右子节点最大值的编号val
		if (a[t[lc(p)].val] >= a[t[rc(p)].val]) {
			t[p].val = t[lc(p)].val;
		} else {
			t[p].val = t[rc(p)].val;
		}

		// size保存左右子节点size的和
		t[p].size = t[lc(p)].size + t[rc(p)].size;
	}

	// 区间查询
	// p表示树节点编号
	// l、r表示查询的区间
	// k表示区间范围限制
	public static int findMax(int p, int l, int r, long k) {
		
		if (l == r) {
			// 找到叶子节点，直接返回原数组对应下标
			return l;
		}
		// 折半找节点区间[l,r]的中点
		int mid = (l + r) >> 1;
		// 找的范围如果小于左子树，则直接返回左边的结果，忽略右边子树
		if (k <= t[lc(p)].size) {
			return findMax(lc(p), l, mid, k);
		}
		// 找右边子树答案
		int ans = findMax(rc(p), mid + 1, r, k - t[lc(p)].size);
		// 左边最大值和右边最大值比较，返回大的下标
		if (a[t[lc(p)].val] >= a[ans]) {
			return t[lc(p)].val;
		} else {
			return ans;
		}

	}

	// p表示树节点编号
	// l、r表示查询的区间
	// x表示需要删除的数在原数组的下标
	// 相当于线段树单点修改，维护树节点size的值
	public static void change(int p, int l, int r, int x) {
		
		
		if (l == r) {
			t[p].size = 0;
			return;
		}
		// 折半找节点区间[l,r]的中点
		int mid = (l + r) >> 1;

		if (x <= mid) {
			// 如果位置在左子树
			change(lc(p), l, mid, x);
		} else {
			// 如果位置在右子树
			change(rc(p), mid + 1, r, x);
		}
		pushUp(p);
	}

	// p表示树节点编号
	// l、r表示查询的区间
	// x表示删除的数在原数组的下标
	// 相当计算删除的数左边可用数据的个数
	public static int count(int p, int l, int r, int x) {
		
		if (l == r) {
			//找到叶子节点了
			return t[p].size;
		}
		// 折半找节点区间[t[p].l,t[p].r]的中点
		int mid = (l + r) >> 1;

		if (x <= mid) {
			// 如果位置在左子树
			return count(lc(p), l, mid, x);
		} else {
			// 如果位置在右子树
			// 返回当前左子数长度+右子树
			return t[lc(p)].size + count(rc(p), mid + 1, r, x);
		}
	
	}

	//输入优化模板开始
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
	static int rd() throws IOException {
		st.nextToken();
		return (int) st.nval;
	}
	//输入优化模板结束
	
	//输出优化
	static StringBuilder sb = new StringBuilder();
	
	public static void main(String[] args) throws IOException {
		String[] lineArr = br.readLine().split(" ");
		
		int n = Integer.parseInt(lineArr[0]);
		long m = Long.parseLong(lineArr[1]);
		
		a = new int[n+1];
		int[] ans = new int[n+1];
		t = new Tree[4*n+1];
		
		for(int i=1;i<=n;i++) {
			a[i] = rd();
		}

		build(1,1,n);
		
		for(int i=1;i<=n;i++) {
			int tmp = findMax(1,1,n,m+1);
			ans[i] = a[tmp];
			a[tmp] = -1;
			change(1,1,n,tmp);
			m -= count(1,1,n,tmp);
		}
		
		for(int i=1;i<=n;i++) {
			sb.append(ans[i]);
			sb.append(" ");
		}
		
		System.out.print(sb.toString());
	}

}
